package com.example.mysharding.algo;

import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ntp.TimeStamp;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class BaseShardingIdAndTimeComplexAlgo implements ComplexKeysShardingAlgorithm<String> {

    protected static final int SHARDING_SUFFIX_LENGTH = 6;

    protected static final String TIME_LOWER_BOUND = "1970-01-01 08:00:00";

    @Override
    public Collection<String> doSharding(Collection<String> targetNames,
                                         ComplexKeysShardingValue<String> complexKeysShardingValue) {
        return null;
    }

    protected Collection<String> baseDoSharding(String tableName, String idKey, String timeKey, Collection<String> targetNames,
                                                ComplexKeysShardingValue<String> complexKeysShardingValue, Integer dataSourceSize, String dsStartegyClass) {
        Set<String> logicTableNames = Sets.newHashSet();
        // 根据id分片,无id根据time分片
        Collection idList = complexKeysShardingValue.getColumnNameAndShardingValuesMap().getOrDefault(idKey,
                null);
        Range<String> timeRange = complexKeysShardingValue.getColumnNameAndRangeValuesMap().getOrDefault(timeKey,
                null);
        if (!CollectionUtils.isEmpty(idList)) {
            for (Object id : idList) {
                logicTableNames.add(tableName +  "_"  +getDateSuffix(id));
            }
            log.info("Sharding predicate:{},result:{},taskIds:{}", new Gson().toJson(
                    complexKeysShardingValue), logicTableNames, idList);
        } else if (timeRange != null) {
            // 根据时间分片
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Object lowerTime = timeRange.hasLowerBound() ? timeRange.lowerEndpoint() : TIME_LOWER_BOUND;
            Object upperTime = timeRange.hasUpperBound() ? timeRange.upperEndpoint() : sdf.format(new Date());
            try {
                logicTableNames.addAll(getTableTimeRange(tableName, getCalendarOfDateString(lowerTime),
                        getCalendarOfDateString(upperTime), dataSourceSize));
                log.info("Sharding predicate:{},result:{},time range:{}-{}", new Gson().toJson(
                        complexKeysShardingValue), logicTableNames, lowerTime, upperTime);
            } catch (Exception e) {
                log.error("Sharding parse time error:{},shardingValue:{},lowerTime:{},uppperTime:{}", e.getMessage(),
                        new Gson().toJson(complexKeysShardingValue), new Gson().toJson(lowerTime),
                        new Gson().toJson(upperTime));
                logicTableNames.addAll(targetNames);
            }
        } else {
            return targetNames;
        }
        // 对命中的表做合法性判断
        return logicTableNames.stream().filter(targetNames::contains).collect(Collectors.toList());
    }
    protected Calendar getCalendarOfDateString(Object date) throws ParseException {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat sdfWithSec = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        SimpleDateFormat sdfWithoutSec = new SimpleDateFormat("yyyy-MM-dd");
        if (date instanceof String) {
            String dateStr = (String) date;
            if (dateStr.length() > 10) {
                c.setTime(sdfWithSec.parse(dateStr.trim()));
            } else {
                c.setTime(sdfWithoutSec.parse(dateStr.trim()));
            }
        } else if (date instanceof Date) {
            Date d = (Date) date;
            c.setTime(d);
        } else if (date instanceof TimeStamp) {
            TimeStamp ts = (TimeStamp) date;
            c.setTime(ts.getDate());
        }
        return c;
    }
    protected List<String> getTableTimeRange (String tableName, Calendar lowerTime, Calendar upperTime, Integer dataSourceSize) {
        int dataSourceIndex = dataSourceSize - 1;
        List<String> targetTableNames = Lists.newArrayList();
        if (lowerTime.get(Calendar.YEAR) == upperTime.get(Calendar.YEAR)) {
            for (int i = lowerTime.get(Calendar.MONTH); i <= upperTime.get(Calendar.MONTH); i++) {
                targetTableNames.add(new StringBuilder(tableName + "_").append(lowerTime.get(Calendar.YEAR))
                        .append(String.format("%02d", i + 1)).toString());
            }
        } else {
            int i;
            // 下限时间当年所有表
            for (i = lowerTime.get(Calendar.MONTH); i < 12; i++) {
                targetTableNames.add(new StringBuilder(tableName+ "_").append(lowerTime.get(Calendar.YEAR))
                        .append(String.format("%02d", i + 1)).toString());
            }
            // 上限下限中间所有表
            for (i = lowerTime.get(Calendar.YEAR) + 1; i < upperTime.get(Calendar.YEAR); i++) {
                for (int j = 1;j < 13; j++) {
                    targetTableNames.add(new StringBuilder(tableName + "_").append(String.format("%04d", i))
                            .append(String.format("%02d", j)).toString());
                }
            }
            // 上限时间当年表
            for (i = 0; i <= upperTime.get(Calendar.MONTH); i++) {
                targetTableNames.add(new StringBuilder(tableName + "_").append(upperTime.get(Calendar.YEAR))
                        .append(String.format("%02d", i + 1)).toString());
            }
        }
        return targetTableNames;
    }

    protected String getDateSuffix(String shardingValue) {
        if (shardingValue.length() < 6) {
            log.error("ComplexAlgo: sharding keys' value is illegal");
            return "";
        }
        return "_" + shardingValue.substring(0, SHARDING_SUFFIX_LENGTH);
    }

    private String getDateSuffix(Long shardingValue){
        if (shardingValue.toString().length() < 6) {
            log.error("ComplexAlgo: sharding keys' value is illegal");
            return "";
        }
        return shardingValue.toString().substring(0, SHARDING_SUFFIX_LENGTH);
    }

    private String getDateSuffix(Object shardingValue) {
        if (shardingValue instanceof String) {
            return getDateSuffix((String) shardingValue);
        } else if (shardingValue instanceof Long) {
            return getDateSuffix((Long) shardingValue);
        } else {
            log.error("ComplexAlgo: sharding keys' value is illegal");
            return "";
        }
    }
}
